findMdpInsert.js ➔ findMdpInsert   C
last analyzed

Complexity

Conditions 7
Paths 6

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 7
c 2
b 0
f 0
nc 6
nop 2
dl 0
loc 26
rs 6.7272
1
/** findMdpInsert and findCode functions use a similar layout to return the location and contents
2
  *   .start          => points at the character in the string why the other item starts (ie. comment or code block)
3
  *   .length         => is the overall length of the comment or code block.
4
  *   .internalStart  => points at the character in the string where the internal payload starts
5
  *   .internalLength => is the length of the internal payload
6
  *   .commandString  => is the command string found within the particular item
7
  *   .info           => is a structure containing further info about what was found
8
  * if start is returned as -1 then nothing was found
9
  *
10
  * The internalStart/internaLength defines the internal content which will be replaced. This does not include
11
  * leading and lagging CRLF/LF. So the replacement text is not required to have either leading or lagging line
12
  * endings. However, if the internalLength is negative this means that leading CRLF or LF must be added by the insertion
13
  * routine. The reason for this is that it allows insertions between code fences or mdpInsert pairs which have zero lines
14
  * between them.
15
  *
16
**/
17
18
const {findCode} = require('./findCode.js')
19
20
export function findMdpInsert (txt, start) {
21
  let s = _findMdpStartUnfenced(txt, start)
22
  if (s.start === -1) { return s }
23
  let s1 = JSON.parse(JSON.stringify(s)) // create copy
24
  let depth = 1
25
  let e
26
  let posn = s1.internalStart - 2
27
  do {
28
    e = _findMdpEndUnfenced(txt, s, posn)
29
    if (e.start === -1) {
30
      // we have not found any more ends so we need to return a fail
31
      return e
32
    }
33
    s1 = _findMdpStartUnfenced(txt, posn)
34
    if (s1.start !== -1 && s1.start < (e.internalStart + e.internalLength)) {
35
      // we have found another start pattern before the end of the end pattern - so a new level
36
      depth++
37
      posn = s1.internalStart - 2
38
    } else {
39
      depth--
40
      posn = e.start + e.length
41
    }
42
    if (depth > 5) { return {start: -1} }
43
  } while (depth !== 0)
44
  return e
45
}
46
47
function _findMdpStartUnfenced (txt, start) {
48
  let lookFrom = start
49
  let m, c
50
  while (true) {
51
    m = _findMdpStart(txt, lookFrom)
52
    if (m.start === -1) { return m }
53
    // we need to find the first code which ends after the mdp start
54
    c = _findCodeEndingAfter(txt, lookFrom, m.start)
55
    if (c.start === -1 || m.start < c.start) {
56
      // the mdp start we've found is not within a code fence
57
      break
58
    }
59
    // the mdp start we've found is within a code fence so find the next one
60
    lookFrom = c.start + c.length
61
  }
62
  return m
63
64
  function _findMdpStart (txt, start) {
65
    let regex = /(\r\n|\n|^)([ ]{0,3}\[>[^\r\n\t\0[\]]*\]: # (\([^\r\n\t\0]*\)|"[^\r\n\t\0]*"|'[^\r\n\t\0]*'))(\r\n|\n)/g
66
    regex.lastIndex = start
67
    let regexResult = regex.exec(txt)
68
    if (regexResult === null) { return {start: -1} }
69
    let r = {
70
      start: regexResult.index + regexResult[1].length,
71
      internalStart: regexResult.index + regexResult[0].length,
72
      commandString: regexResult[3].substring(1, regexResult[3].length - 1)
73
    }
74
    return r
75
  }
76
}
77
78
function _findMdpEndUnfenced (txt, opening, start) {
79
  let lookFrom = start
80
  let m, c
81
  while (true) {
82
    m = _findMdpEnd(txt, opening, lookFrom - 2)
83
    if (m.start === -1) { return m }
84
    // we need to find the first code which ends after the mdpEnd starts
85
    c = _findCodeEndingAfter(txt, lookFrom, (m.internalStart + m.internalLength))
86
    if (c.start === -1 || (m.internalStart + m.internalLength) < c.start) { break } // the mdp end we've found is not within a code fence
87
    // the mdp end we've found is within a code fence so find the next one
88
    lookFrom = c.start + c.length
89
  }
90
  return m
91
92
  function _findMdpEnd (txt, opening, start) {
93
    let r = JSON.parse(JSON.stringify(opening)) // create copy of opening structure passed in
94
    let regex = /(\r\n|\n)([ ]{0,3}\[<[^\r\n\t\0[\]]*\]: #)(\r\n|\n|$)/g
95
    regex.lastIndex = start
96
    let regexResult = regex.exec(txt)
97
    if (regexResult === null) { return {start: -1} }
98
    r.internalLength = regexResult.index - r.internalStart
99
    r.length = regexResult.index + regexResult[0].length - regexResult[3].length - r.start
100
    return r
101
  }
102
}
103
104
function _findCodeEndingAfter (txt, start, endingAfter) {
105
  // returns the first code block which ends after the position specified
106
  // the search starts at start
107
  let pos = start
108
  let c
109
  do {
110
    c = findCode(txt, pos)
111
    pos = c.start + c.length
112
  } while (c.start !== -1 && pos <= endingAfter)
113
  return c
114
}
115